﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.ComponentModel;
using System.Globalization;

namespace Xceed.Silverlight.DataGrid.Converters
{
  internal class EnumConverter : TypeConverter
  {
    public EnumConverter( Type type )
    {
      if( type == null )
        throw new ArgumentNullException( "type" );

      if( !type.IsEnum )
        throw new ArgumentException( "The specified type must be an enumeration.", "type" );

      this.EnumType = type;
      this.UnderlyingType = Enum.GetUnderlyingType( type );
      this.UnderlyingTypeConverter = this.GetUnderlyingTypeConverter( this.UnderlyingType );
    }

    private Type EnumType
    {
      get;
      set;
    }

    private Type UnderlyingType
    {
      get;
      set;
    }

    private TypeConverter UnderlyingTypeConverter
    {
      get;
      set;
    }

    public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType )
    {
      if( sourceType == null )
        return false;

      if( ( typeof( string ) == sourceType )
        || ( typeof( Enum[] ) == sourceType )
        || ( this.EnumType == sourceType )
        || ( this.UnderlyingType == sourceType ) )
        return true;

      if( ( this.UnderlyingTypeConverter != null )
        && ( this.UnderlyingTypeConverter.CanConvertFrom( context, sourceType ) ) )
        return true;

      return false;
    }

    public override bool CanConvertTo( ITypeDescriptorContext context, Type destinationType )
    {
      return this.CanConvertFrom( context, destinationType );
    }

    public override object ConvertFrom( ITypeDescriptorContext context, CultureInfo culture, object value )
    {
      if( value != null )
      {
        Type valueType = value.GetType();

        if( valueType == this.EnumType )
          return value;

        if( valueType == this.UnderlyingType )
          return Enum.ToObject( this.EnumType, Convert.ToInt64( value, culture ) );

        if( valueType == typeof( string ) )
        {
          string stringValue = ( string )value;

          // Handle an enumeration that has flags.
          if( stringValue.IndexOf( ',' ) < 0 )
          {
            return Enum.Parse( this.EnumType, stringValue, true );
          }
          else
          {
            long numericValue = 0;

            foreach( string stringPart in stringValue.Split( new char[] { ',' } ) )
            {
              numericValue |= Convert.ToInt64( ( Enum )Enum.Parse( this.EnumType, stringPart, true ), culture );
            }

            return Enum.ToObject( this.EnumType, numericValue );
          }
        }

        if( valueType == typeof( Enum[] ) )
        {
          long numericValue = 0;

          foreach( Enum enumValue in ( Enum[] )value )
          {
            numericValue |= Convert.ToInt64( enumValue, culture );
          }

          return Enum.ToObject( this.EnumType, numericValue );
        }

        if( ( this.UnderlyingTypeConverter != null )
          && ( this.UnderlyingTypeConverter.CanConvertFrom( valueType ) ) )
          return Enum.ToObject( this.EnumType, Convert.ToInt64( value, culture ) );
      }

      throw new InvalidOperationException( "The conversion cannot be performed." );
    }

    public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType )
    {
      if( destinationType == null )
        throw new ArgumentNullException( "destinationType" );

      if( value != null )
      {
        Type valueType = value.GetType();

        if( destinationType == valueType )
          return value;

        if( valueType != this.EnumType )
        {
          if( !this.CanConvertFrom( valueType ) )
            throw new InvalidOperationException( "The conversion cannot be performed." );

          value = this.ConvertFrom( context, culture, value );
          valueType = value.GetType();
        }

        if( destinationType == this.EnumType )
          return value;

        if( destinationType == typeof( Enum[] )
          && value is Enum )
          return new Enum[] { ( Enum )value };

        if( ( this.UnderlyingTypeConverter != null )
          && ( this.UnderlyingTypeConverter.CanConvertTo( destinationType ) ) )
          return this.UnderlyingTypeConverter.ConvertTo( context, culture, value, destinationType );

        if( destinationType == typeof( string ) )
          return value.ToString();
      }
      else if( destinationType == typeof( Enum[] ) )
      {
        return new Enum[] { };
      }

      throw new InvalidOperationException( "The conversion cannot be performed." );
    }

    private TypeConverter GetUnderlyingTypeConverter( Type underlyingType )
    {
      if( underlyingType == null )
        return null;

      try
      {
        return new PrimitiveConverter( this.UnderlyingType );
      }
      catch( Exception )
      {
      }

      return null;
    }
  }
}
